package org.senseframework.support.orm.mybatis.query.builder;

import org.apache.commons.lang3.StringUtils;
import org.senseframework.support.orm.mybatis.annotations.Column;
import org.senseframework.support.orm.mybatis.annotations.Table;
import org.senseframework.support.orm.mybatis.definition.EntityColumnDefinition;
import org.senseframework.support.orm.mybatis.definition.EntityDefinition;
import org.senseframework.support.orm.mybatis.definition.Update;

import java.lang.reflect.Field;
import java.util.*;

/**
 * Author lanjian
 * Email  jianlan@iflytek.com
 */
public abstract class BaseQuery extends HashMap {

    private static HashMap<Class, EntityDefinition> entityDefinitions = new HashMap<Class, EntityDefinition>();

//    private MysqlBaseMapper mysqlBaseMapper;
//
//    public MysqlBaseMapper getMysqlBaseMapper() {
//        if (mysqlBaseMapper == null) {
//            throw new RuntimeException("MysqlBaseMapper is null");
//        }
//        return mysqlBaseMapper;
//    }

//    public void setMysqlBaseMapper(MysqlBaseMapper mysqlBaseMapper) {
//        this.mysqlBaseMapper = mysqlBaseMapper;
//    }

    private static EntityDefinition table(Class clazz) {
        if (clazz.isAnnotationPresent(Table.class)) {
            EntityDefinition entityDefinition = new EntityDefinition();
            Table table = (Table) clazz.getAnnotation(Table.class);
            entityDefinition.setTable(table.table());
            entityDefinition.setTableAs(table.tableAs());
            entityDefinition.setJoinTable(table.joinTable());
            entityDefinition.setJoinTableAs(table.joinTableAs());
            entityDefinition.setJoinTableOn(table.joinTableOn());
            entityDefinition.setTableOn(table.tableOn());
            entityDefinition.setJoinType(table.joinType());
            entityDefinition.setId(table.id());
            return entityDefinition;
        }
        throw new RuntimeException("not  table");
    }

    private static void registerColumns(List<Field> fields, EntityDefinition entityDefinition) {
        for (Field field : fields) {
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                EntityColumnDefinition columnDefinition = new EntityColumnDefinition();
                if (StringUtils.isBlank(column.column())) {
                    columnDefinition.setColumn(field.getName());
                } else {
                    columnDefinition.setColumn(column.column());
                }
                columnDefinition.setJavaColumn(field.getName());
                columnDefinition.setColumnAs(column.columnAs());
                columnDefinition.setType(field.getType());
                columnDefinition.setTableAs(column.tableAs());
                columnDefinition.setLength(column.length());
                columnDefinition.setId(column.id());
                columnDefinition.setNullable(column.nullable());
                columnDefinition.setDesc(column.desc());
                entityDefinition.addEntityColumnDefinition(field.getName(), columnDefinition);
            }
        }
    }

    protected static List<Field> allFields(Class cls) {
        List<Field> all = new ArrayList<Field>();
        all.addAll(Arrays.asList(cls.getDeclaredFields()));
        if (cls.getSuperclass() != null) {
            all.addAll(Arrays.asList(cls.getSuperclass().getDeclaredFields()));
        }
        return all;
    }

    public static EntityDefinition resolve(Class clazz) {
        EntityDefinition entityDefinition = table(clazz);
        registerColumns(allFields(clazz), entityDefinition);
        return entityDefinition;
    }

    private Class clazz;

    public BaseQuery(Class clazz) {
        if (clazz == null) {
            throw new RuntimeException("class is null");
        }
        synchronized (this) {
            if (!entityDefinitions.containsKey(clazz)) {
                entityDefinitions.put(clazz, resolve(clazz));
            }
        }
        this.clazz = clazz;
    }

    public EntityDefinition findEntityDefinition() {
        EntityDefinition entityDefinition = entityDefinitions.get(clazz);
        if (entityDefinition == null) {
            entityDefinition = resolve(clazz);
            entityDefinitions.put(clazz, entityDefinition);
        }
        return entityDefinition;
    }


    public void set(String property, Object value, List<Update> updates, Map map) {
        set(property, property, value, updates, map);
    }

    public void set(String property, String propertyAs, Object value, List<Update> updates, Map map) {
        synchronized (map) {
            Update update = new Update(value, property, propertyAs);
            if (!updates.contains(update)) {
                updates.add(update);
            }
        }
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }
}
